package org.slf4j;

import com.alibaba.ttl.TransmittableThreadLocal;
import org.slf4j.helpers.ThreadLocalMapOfStacks;
import org.slf4j.spi.MDCAdapter;

import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName TtlMDCAdapter
 * @Description
 *      重构{@link LogbackMDCAdapter}类，搭配TransmittableThreadLocal实现父子线程之间的数据传递
 *
 *      LogbackMDCAdapter 是 Logback 日志框架中的一个类，作为 MDC（Mapped Diagnostic Context） 的适配器。
 *      MDC 是一种将诊断信息（例如当前线程的用户 ID、事务 ID 等）关联到当前线程的机制，通常用于日志记录中，以便在日志中跟踪和分析特定的上下文信息。
 *
 *      Logback 是一个广泛使用的 Java 日志框架，LogbackMDCAdapter 主要用于适配 MDC 的实现，使得能够在 Logback 中正确地操作和记录与线程相关的上下文信息。
 * @author: zhijianpu
 * @Date: 2025/2/25
 */
public class TtlMDCAdapter implements MDCAdapter {

    final ThreadLocal<Map<String, String>> readWriteThreadLocalMap = new TransmittableThreadLocal<>();

    final ThreadLocal<Map<String, String>> readOnlyThreadLocalMap = new TransmittableThreadLocal<>();

    /**
     * ThreadLocalMapOfStacks 是 SLF4J（Simple Logging Facade for Java）中的一个类，属于 SLF4J 内部的帮助类，
     *
     * 用于实现线程本地存储（ThreadLocal）和栈（Stack）数据结构的组合。
     *
     * 这个类不是 SLF4J API 的一部分，它是 SLF4J 实现的内部数据结构，通常用于在多线程环境中维护和管理线程特定的日志上下文。
     */
    private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks();

    private static TtlMDCAdapter mtcMDCAdapter;

    static {
        mtcMDCAdapter = new TtlMDCAdapter();
        MDC.mdcAdapter = mtcMDCAdapter;
    }

    public static MDCAdapter getInstance() {
        return mtcMDCAdapter;
    }

    private void nullifyReadOnlyThreadLocalMap() {
        readOnlyThreadLocalMap.set(null);
    }

    @Override
    public void put(String key, String val) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null");
        }

        Map<String, String> current = readWriteThreadLocalMap.get();

        if (current == null) {
            current = new HashMap<>();
            readWriteThreadLocalMap.set(current);
        }

        current.put(key, val);
        nullifyReadOnlyThreadLocalMap();
    }

    @Override
    public String get(String key) {
        Map<String, String> hashMap = readWriteThreadLocalMap.get();

        if ((hashMap != null) && (key != null)) {
            return hashMap.get(key);
        } else {
            return null;
        }
    }

    @Override
    public void remove(String key) {
        if (key == null) {
            return;
        }

        Map<String, String> current = readWriteThreadLocalMap.get();
        if (current != null) {
            current.remove(key);
            nullifyReadOnlyThreadLocalMap();
        }
    }

    @Override
    public void clear() {
        readWriteThreadLocalMap.set(null);
        nullifyReadOnlyThreadLocalMap();
    }

    @SuppressWarnings("unchecked")
    public Map<String, String> getPropertyMap() {
        Map<String, String> readOnlyMap = readOnlyThreadLocalMap.get();
        if (readOnlyMap == null) {
            Map<String, String> current = readWriteThreadLocalMap.get();
            if (current != null) {
                final Map<String, String> tempMap = new HashMap<>(current);
                readOnlyMap = Collections.unmodifiableMap(tempMap);
                readOnlyThreadLocalMap.set(readOnlyMap);
            }
        }
        return readOnlyMap;
    }

    @Override
    public Map<String, String> getCopyOfContextMap() {
        Map<String, String> readOnlyMap = getPropertyMap();
        if (readOnlyMap == null) {
            return null;
        } else {
            return new HashMap<>(readOnlyMap);
        }
    }

    @Override
    public void setContextMap(Map<String, String> contextMap) {
        if (contextMap != null) {
            readWriteThreadLocalMap.set(new HashMap<String, String>(contextMap));
        } else {
            readWriteThreadLocalMap.set(null);
        }
        nullifyReadOnlyThreadLocalMap();
    }

    @Override
    public void pushByKey(String key, String value) {
        threadLocalMapOfDeques.pushByKey(key, value);
    }

    @Override
    public String popByKey(String key) {
        return threadLocalMapOfDeques.popByKey(key);
    }

    @Override
    public Deque<String> getCopyOfDequeByKey(String key) {
        return threadLocalMapOfDeques.getCopyOfDequeByKey(key);
    }

    @Override
    public void clearDequeByKey(String key) {
        threadLocalMapOfDeques.clearDequeByKey(key);
    }
}
